# -*- coding: utf-8 -*-
import base64
import json
import urllib
from collections import OrderedDict

import requests
import xmltodict
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
from Crypto.Hash import MD5
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5 as Signature_pkcs1_v1_5
from django.conf import settings

from async import async_job
from common.cache import redis_cache
from common.order import db as order_db
from common.order.model import PAY_STATUS
from common.utils import track_logging
from common.utils.exceptions import ParamError
from common.utils.tz import local_now

_LOGGER = track_logging.getLogger(__name__)

APP_CONF = {
    '101001004021': {  # 大菠萝 支付宝3.4%（1---3000）D1结算
        'gateway': 'https://api.yourton.net/gateway/api/h5apipay',  # V3.1接口
        'gateway1': 'https://pay.yourton.net/gateway?input_charset=UTF-8',  # V3.0接口
        'query_gateway': 'https://query.yourton.net/query',
        'pub_key': """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/oZKEAs/F8KPzY2/8+Sf5oPnr
ditxGi4Bj0R2zUYMTeQfq7cUsjBAu96HyfTJTSC+iT1twyVXdSZ9aktSB+n/0+jO
j3QW1PyiX1BwOgYAq59PIltPH8X1srpX422Akvx/AvLh6CwRZoR5rRAB5vHBO86u
r3eNAr1qO9QxR4tltQIDAQAB
-----END PUBLIC KEY-----""",
        'pri_key': """-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAL+hkoQCz8Xwo/Nj
b/z5J/mg+et2K3EaLgGPRHbNRgxN5B+rtxSyMEC73ofJ9MlNIL6JPW3DJVd1Jn1q
S1IH6f/T6M6PdBbU/KJfUHA6BgCrn08iW08fxfWyulfjbYCS/H8C8uHoLBFmhHmt
EAHm8cE7zq6vd40CvWo71DFHi2W1AgMBAAECgYEAqgXN/EiOVl4a6SCJxRa/bBFy
jIJtKs2CkimuIgYhJ1uYkq0FJFywyU6msy3xfQVsjwEvOorRP9aWTuIXb/HLn3/j
JIH6YOFnrrWQtbtpWRMLbMpu6jn0iBjhobqYKV/3taG58siwQjHW0yYK2KjT9ZuJ
tHUSCrDJSXIRA2c/pkUCQQD2/VvjanZRPlNJIoyZHqindJPiuupmVCAREB4G3FBx
95G7eUnKj7e59pi0XMRZ+ib0FBhva1VuEr/FgzpfNKFfAkEAxp84Dyi10eagtLvg
kpPA1R0fpZfFK0jfRX4ZoiCGU91qPL0h/XwKNL/n7sEvVOI6+EKKUM1M1B83PmD3
SzntawJBAOk2F2JnLurNd6SRu8IEwejD2bN3HEac3l5eIowF0OubgH5NKtDZ3Pf5
kPyUr03qNoe2xc/GwDab+3mKzD4DGu8CQBDV4BeQ2NCA3vBS2Hc0JO0reco8cg74
L0yhoqSWYyxqoR2i3mWxBOBxv8QCBfQI9PykDztIG5IDT1hvOabbsx8CQHjTTj2m
J4a3dmtQJ63i8Qyvk0FOzPA1GSuQ4hrALr36FARoUd2dlowsO7s9VUoWtswP82Rv
yjnXrPPlUyMTNTc=
-----END PRIVATE KEY-----""",
        'yt_pub': '''-----BEGIN PUBLIC KEY-----
        MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+wYxBehfMLijSfSTZuOxHNQyxB+UCkteITEAO3EwoE53sCXcnAuwRWB+pCTOrBLe2NcrCdIu51Jk6L3Hc7mNcFoeSNxRM6lKYXDEFV0j1jOaqDn4XlHP3AVAbZLZPjMSDmuQlgtoy8ygJSil/gjyW3xlWzcnTP1DKcT6sxwn9aQIDAQAB
        -----END PUBLIC KEY-----''',
    },
}


def _get_api_key(mch_id):
    return APP_CONF[mch_id]['API_KEY']


def _get_gateway(mch_id):
    return APP_CONF[mch_id]['gateway']


def _get_gateway1(mch_id):
    return APP_CONF[mch_id]['gateway1']


def _get_query_gateway(mch_id):
    return APP_CONF[mch_id]['query_gateway']


def _get_pub_key(mch_id):
    return APP_CONF[mch_id]['pub_key']


def _get_pri_key(mch_id):
    return APP_CONF[mch_id]['pri_key']


def _get_ytpub_key(mch_id):
    return APP_CONF[mch_id]['yt_pub']


def rsa_long_encrypt(pub_key_str, msg, length=100):
    """
    单次加密串的长度最大为 (key_size/8)-11
    1024bit的证书用100， 2048bit的证书用 200
    """
    pubobj = RSA.importKey(pub_key_str)
    pubobj = Cipher_pkcs1_v1_5.new(pubobj)
    res = []
    for i in range(0, len(msg), length):
        res.append(pubobj.encrypt(msg[i:i + length]))
    return "".join(res)


def rsa_long_decrypt(priv_key_str, msg, length=128):
    """
    1024bit的证书用128，2048bit证书用256位
    """
    privobj = RSA.importKey(priv_key_str, passphrase='614360')
    privobj = Cipher_pkcs1_v1_5.new(privobj)
    res = []
    for i in range(0, len(msg), length):
        res.append(privobj.decrypt(msg[i:i + length], 'xyz'))
    return "".join(res)


def _gen_sign(message, key):
    key = RSA.importKey(key)
    h = MD5.new(message)
    signer = Signature_pkcs1_v1_5.new(key)
    signature = signer.sign(h)
    return base64.b64encode(signature)


def _verify_sign(data, signature, key):
    key = RSA.importKey(key)
    h = MD5.new(data)
    verifier = Signature_pkcs1_v1_5.new(key)
    if verifier.verify(h, base64.b64decode(signature)):
        return True
    return False


def generate_rsa_sign_str(parameter):
    s = ''
    for k in sorted(parameter.keys()):
        s += '%s=%s&' % (k.encode('utf8'), parameter[k].encode('utf8'))
    return s[0:len(s) - 1]


def _get_device_ip(info):
    try:
        extra = json.loads(info['extra'])
    except:
        extra = {}
    user_info = extra.get('user_info', {})
    return user_info.get('device_ip') or '127.0.0.1'


def _get_pay_type(service):
    if service == 'wxpay':
        return 'weixin_h5api'
    elif service == 'alipay1':  # V3.0接口
        return 'h5_ali'
    else:
        return 'alipay_h5api'  # V3.1接口


def _build_form(params, gateway):
    html = u"<head><title>loading...</title></head><form id='submit' name='submit' action='" + gateway + "' method='post'>"
    for k, v in params.items():
        html += "<input type='hidden' name='%s' value='%s'/>" % (k, v)
    html += "</form>"
    html += "<script>doc" \
            "ument.forms['submit'].submit();</script>"
    return html


def create_charge_v30(pay, pay_amount, info):
    service = info.get('service')
    app_id = info['app_id']
    parameter_dict = OrderedDict((
        ('merchant_code', app_id),
        ('service_type', _get_pay_type(service)),
        ('notify_url', '{}/pay/api/{}/yingtongpay/{}/'.format(settings.NOTIFY_PREFIX, settings.NOTIFY_PATH, app_id)),
        ('input_charset', 'UTF-8'),
        ('interface_version', 'V3.0'),
        ('order_no', str(pay.id)),
        ('order_time', local_now().strftime('%Y-%m-%d %H:%M:%S')),
        ('order_amount', '%.2f' % pay_amount),
        ('product_name', 'CHARGE'),
    ))
    sign_str = generate_rsa_sign_str(parameter_dict)
    parameter_dict['sign'] = _gen_sign(sign_str, _get_pri_key(app_id))
    parameter_dict['sign_type'] = 'RSA-S'
    html_text = _build_form(parameter_dict, _get_gateway1(app_id))
    cache_id = redis_cache.save_html(pay.id, html_text)
    url = settings.PAY_CACHE_URL + cache_id
    _LOGGER.info("yingtongpay create: %s, url: %s", parameter_dict, url)
    return {'charge_info': url}


def create_charge(pay, pay_amount, info):
    service = info.get('service')
    if service == 'alipay1':
        return create_charge_v30(pay, pay_amount, info)

    app_id = info['app_id']
    parameter_dict = OrderedDict((
        ('merchant_code', app_id),
        ('service_type', _get_pay_type(service)),
        ('notify_url', '{}/pay/api/{}/yingtongpay/{}/'.format(settings.NOTIFY_PREFIX, settings.NOTIFY_PATH, app_id)),
        ('interface_version', 'V3.1'),
        ('client_ip', _get_device_ip(info)),
        ('order_no', str(pay.id)),
        ('order_time', local_now().strftime('%Y-%m-%d %H:%M:%S')),
        ('order_amount', '%.2f' % pay_amount),
        ('product_name', 'CHARGE'),
    ))
    sign_str = generate_rsa_sign_str(parameter_dict)
    parameter_dict['sign'] = _gen_sign(sign_str, _get_pri_key(app_id))
    parameter_dict['sign_type'] = 'RSA-S'
    _LOGGER.info("yingtongpay create: %s", parameter_dict)
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    response = requests.post(_get_gateway(app_id), data=parameter_dict, headers=headers, timeout=5)
    _LOGGER.info("yingtongpay create charge rsp: %s %s", response.status_code, response.text)
    d = xmltodict.parse(response.text)
    _LOGGER.info('yingtongpay create rspd :%s', d)
    return {'charge_info': urllib.unquote(d[u'dinpay'][u'response'][u'payURL']).replace('HTTPS://QR.ALIPAY.COM/',
                                                                                        'https://qr.alipay.com/')}


def verify_notify_sign(params, key):
    sign = params['sign']
    params.pop('sign')
    params.pop('sign_type')
    sign_str = generate_rsa_sign_str(params)
    if not _verify_sign(sign_str, sign, key):
        _LOGGER.info("yingtongpay sign: %s, sign_str : %s", sign, sign_str)
        raise ParamError('sign not pass, data: %s' % params)


# SUCCESS
def check_notify_sign(request, app_id):
    api_key = _get_ytpub_key(app_id)
    data = dict(request.POST.iteritems())
    _LOGGER.info("yingtongpay notify data: %s", data)
    verify_notify_sign(data, api_key)
    pay_id = data['order_no']
    if not pay_id:
        _LOGGER.error("fatal error, out_trade_no not exists, data: %s" % data)
        raise ParamError('yingtongpay event does not contain pay ID')

    pay = order_db.get_pay(int(pay_id))
    if not pay:
        raise ParamError('pay_id: %s invalid' % pay_id)
    if pay.status != PAY_STATUS.READY:
        raise ParamError('pay %s has been processed' % pay_id)

    mch_id = pay.mch_id
    trade_status = data[u'trade_status']
    trade_no = data[u'trade_no']
    total_fee = float(data[u'order_amount'])
    discount = float(json.loads(pay.extend or '{}').get('discount', 0))
    extend = {
        'discount': discount,
        'trade_status': trade_status,
        'trade_no': trade_no,
        'total_fee': total_fee
    }

    from common.channel.pay import check_channel_order
    check_channel_order(pay_id, total_fee, app_id)

    # SUCCESS 交易成功
    # FAILED 交易失败
    if trade_status == 'SUCCESS':
        _LOGGER.info('yingtongpay check order success, mch_id:%s pay_id:%s' % (mch_id, pay_id))
        order_db.add_pay_success(mch_id, pay_id, total_fee, trade_no, extend)
        # async notify
        async_job.notify_mch(pay_id)


# 只能查询十二小时以内订单
def query_charge(pay_order, app_id):
    pay_id = pay_order.id
    parameter_dict = OrderedDict((
        ('service_type', 'single_trade_query'),
        ('merchant_code', app_id),
        ('interface_version', 'V3.0'),
        ('order_no', str(pay_id)),
    ))
    sign_str = generate_rsa_sign_str(parameter_dict)
    parameter_dict['sign'] = _gen_sign(sign_str, _get_pri_key(app_id))
    parameter_dict['sign_type'] = 'RSA-S'
    _LOGGER.info("yingtongpay query: %s", parameter_dict)
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    response = requests.post(_get_query_gateway(app_id), data=parameter_dict, headers=headers, timeout=5)
    _LOGGER.info("yingtongpay query  rsp: %s %s", response.status_code, response.text)
    data = xmltodict.parse(response.text)['dinpay']['response']['trade']
    if response.status_code == 200:
        trade_status = data['trade_status']
        total_fee = float(data['order_amount'])
        trade_no = data['trade_no']
        extend = {
            'trade_status': trade_status,
            'trade_no': trade_no,
            'total_fee': total_fee
        }

        if trade_status == 'SUCCESS':
            _LOGGER.info('yingtongpay query order success, mch_id:%s pay_id:%s' % (pay_order.mch_id, pay_id))
            order_db.add_pay_success(pay_order.mch_id, pay_id, total_fee, trade_no, extend)
            async_job.notify_mch(pay_order.id)
    else:
        _LOGGER.warn('yingtongpay data error, status_code: %s', response.status_code)
